package co.edu.unal.bioingenium.kbmed.knowledge.mapping.heuristic;

import co.edu.unal.bioingenium.kbmed.knowledge.mapping.vo.DescriptorScore;
import co.edu.unal.bioingenium.kbmed.knowledge.vo.DescriptorData;
import static java.lang.Math.pow;
import static java.lang.Math.sqrt;
import java.util.BitSet;

/**
 *
 * @author Luis A Riveros
 */
public class Matching {

    private static double a1 = 0.2, a2 = 0.1, a3 = 0.2, a4 = 0.5;

    /**
     * 
     * @param pa1
     * @param pa2
     * @param pa3
     * @param pa4
     */
    public static void setWeight(double pa1, double pa2, double pa3, double pa4) {
        a1 = pa1;
        a2 = pa2;
        a3 = pa3;
        a4 = pa4;
    }

    /**
     * 
     * @return
     */
    public static double getA1() {
        return a1;
    }

    /**
     * 
     * @return
     */
    public static double getA2() {
        return a2;
    }

    /**
     * 
     * @return
     */
    public static double getA3() {
        return a3;
    }

    /**
     * 
     * @return
     */
    public static double getA4() {
        return a4;
    }

    /**
     * 
     * @param descriptorScore
     * @param maxValues
     * @return
     */
    public static double getOldScore(DescriptorScore descriptorScore, double[] maxValues) {
        double[] scores = descriptorScore.getScores();
        BitSet bitset = descriptorScore.getBitSetKey();
        double matchMean = 0, scaledMean = 0, dist = 0;
        int matchCount = 0, last = -1;
        for (int i = 0; i < scores.length; i++) {
            if (bitset.get(i)) {
                scaledMean += scores[i] / maxValues[i];
                matchMean += scores[i];
                matchCount++;
                if (last != -1) {
                    dist += i - last;
                }
                last = i;
            }
        }
        //TODO bug cuando las palabars en el term hacen match con mas de una palabra en la sentencia por ejemplo 
        //term:cancer de pulmon  sentece:cancer de pulmon ... cancer, hace que el term parezaca mas disperso de lo que es
        //Dist no midfe bien la diferencias no es una medidad de segundo orden??? en el experimento funbciono para sentencias cortas
        //
        scaledMean = scaledMean / (double) matchCount;
        matchMean = matchMean / (double) matchCount;
        dist = dist / (double) bitset.cardinality();
        return (0.5 * (matchMean)
                + 0.25 * (1 / (double) matchCount)
                + 0.25 * (scaledMean)
                + 0.25 * (1 / (dist + 1)))
                * ((double) matchCount / (double) descriptorScore.getDescriptorTokens());
    }

    /**
     * 
     * @param descriptorScore
     * @param maxValues
     * @return
     */
    public static double getScore(DescriptorScore descriptorScore, double[] maxValues) {
        double[] scores = descriptorScore.getScores();
        BitSet bitset = descriptorScore.getBitSetKey();
        double matchMean = 0, scaledMean = 0, desvEst = 0;
        int matchCount = 0, last = -1, termTokens = descriptorScore.getDescriptorTokens();
        double termTokensdiv2 = (double) termTokens / 2;
        for (int i = 0; i < scores.length; i++) {
            if (bitset.get(i)) {
                scaledMean += scores[i] / maxValues[i];
                matchMean += scores[i];
                if (last >= 0) {
                    if (i - (last + 1) != 0) {
                        desvEst += pow((i - last) / termTokensdiv2, 2);
                    }
                }
                last = i;
                matchCount++;
            }
        }
        scaledMean = scaledMean / (double) matchCount;
        matchMean = matchMean / (double) matchCount;
        desvEst = sqrt(desvEst / matchCount);
        return (a1 * (matchMean)
                + a2 * (1 / (double) matchCount)
                + a3 * (scaledMean)
                + a4 * (1 / (desvEst + 1)))
                * ((double) matchCount / (double) descriptorScore.getDescriptorTokens());
    }

    /**
     * 
     * @param descriptorData
     * @param descriptorScore
     * @param maxValues
     * @return
     */
    public static double getScoreV(DescriptorData descriptorData, DescriptorScore descriptorScore, double[] maxValues) {

        System.out.println("descriptorId: " + descriptorData.getDescriptorId() + " term: " + descriptorData.getTerm());
        double[] scores = descriptorScore.getScores();
        for (int i = 0; i < scores.length; i++) {
            System.out.print(scores[i] + " ");
        }
        System.out.println("");

        BitSet bitset = descriptorScore.getBitSetKey();
        double matchMean = 0, scaledMean = 0, desvEst = 0;
        int matchCount = 0, last = -1, termTokens = descriptorScore.getDescriptorTokens();
        double termTokensdiv2 = (double) termTokens / 2;
        for (int i = 0; i < scores.length; i++) {
            if (bitset.get(i)) {
                scaledMean += scores[i] / maxValues[i];
                matchMean += scores[i];
                if (last >= 0) {
                    if (i - (last + 1) != 0) {
                        desvEst += pow((i - last) / termTokensdiv2, 2);
                    }
                }
                last = i;
                matchCount++;
            }
        }
        scaledMean = scaledMean / (double) matchCount;
        matchMean = matchMean / (double) matchCount;
        desvEst = sqrt(desvEst / matchCount);
        System.out.println("matchMean: " + matchMean);
        System.out.println("(1 / (double) matchCount): " + (1 / (double) matchCount));
        System.out.println("scaledMean: " + scaledMean);
        System.out.println("(1 / (desvEst + 1): " + (1 / (desvEst + 1)));
        System.out.println("matchCount / (double) descriptorScore.getDescriptorTokens(): " + matchCount / (double) descriptorScore.getDescriptorTokens());

        double total = (a1 * matchMean
                + a2 * 1 / (double) matchCount
                + a3 * scaledMean
                + a4 * (1 / (desvEst + 1)))
                * ((double) matchCount / (double) descriptorScore.getDescriptorTokens());
        System.out.println("Total score: " + total);
        return total;
    }

    /**
     * 
     * @param scores
     */
    public static void printScores(double[] scores) {
        for (int i = 0; i < scores.length; i++) {
            System.out.print(" " + scores[i]);
        }
        System.out.println(" ");
    }
}
